﻿#region Creditos

/* Autor: Edilberto Sánchez Forero	*/
/* Email: esanchezfo@gmail.com		*/
/* Licencia: LGPL, Software Libre	*/
/* País: Colombia, Bogotá			*/

#endregion

#region Librerias

using System;
using System.Net.Sockets;

using ESF.General;
using System.Collections.Generic;
using System.Threading;
using System.Net;
using System.IO;
using System.Text;

#endregion


namespace ESF.Peer
{
	public class BasicForwarder : IForwarder
	{
		#region Delegados

		public event Action<Socket> OnEndSend;
		
		#endregion
	
	
		#region Propiedades
		
		public string Name { set; get; }
		
		public bool NoDelay { set; get; }
		
		public int SendTimeoutMS { set; get; }

		public int MaxFileSendSizeBytes { set; get; }

		#endregion
		
		
		#region Constructor
		
		public BasicForwarder () {}
		
		#endregion
		
		
		#region Funciones
			
		public void Send (IPEndPoint pTo, byte [] pData)
		{
			Socket socket;


			if (DataHelper.IsEmpty(pData)) { return; }
						
			socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			socket.NoDelay = NoDelay;
			socket.SendTimeout = SendTimeoutMS;
			socket.Connect (pTo);

			Send (socket, pData);
		}


		public void SendLine(Socket pSenderSocket, string pData)
		{
			Send(pSenderSocket, pData + Environment.NewLine);
		}


		public void Send(Socket pSenderSocket, string pData)
		{
			if (string.IsNullOrWhiteSpace(pData))
			{
				throw new Exception(string.Format("Empty response can not be send to client at {0}", pSenderSocket.RemoteEndPoint));
			}

			if (pData.EndsWith (Environment.NewLine))
			{
				Log.WriteDebug("SEND: " + pData);
			}
			else
			{
				Log.WriteLineDebug("SEND: " + pData);
			}

			Send(pSenderSocket, Encoding.ASCII.GetBytes(pData));
		}


		public void Send (Socket pSocket, byte [] pData)
		{
			if (DataHelper.IsEmpty (pData) || !pSocket.Connected) { return; }

			if (pData.Length > MaxFileSendSizeBytes)
			{
				if (pSocket != null && pSocket.Connected)
				{
					pSocket.Close ();
				}

				throw new Exception(string.Format("Message has {0} bytes, exceeds the Max size of Message to Send {1} bytes", pData.Length, MaxFileSendSizeBytes));
			}

			try
			{
				Console.WriteLine (" -- Connecting to: {0} (NoDelay = {1}, SendTimeout {2}ms), when {3}", pSocket.RemoteEndPoint, NoDelay, pSocket.SendTimeout, DateTime.Now);

				pSocket.BeginSend (pData, 0, pData.Length, 0, new AsyncCallback (SocketSendCallback), pSocket);
			}
			catch (Exception pEx)
			{
				Log.Error (pEx);
			}
		}


		private void SocketSendCallback (IAsyncResult ar) 
		{
			Socket socketHandler;
			
			int bytesSent;


			try 
			{
				// Retrieve the socket from the state object.
				socketHandler = ar.AsyncState as Socket;

				if (!socketHandler.Connected)
				{
					Console.WriteLine (" - Disconnected (SC)");
					return;
				}

				// Complete sending the data to the remote device.
				bytesSent = socketHandler.EndSend (ar);
				
				Console.WriteLine(" - Sent {0} bytes to server.", bytesSent);

				OnEndSend (socketHandler);
			}
			catch (Exception pEx) 
			{
				Log.Error (pEx);
			}
		}

		#endregion
	}
}
